class Vue{
  constructor(options){
    this.$el = options.el;
    this.$data = options.data;
    this.$options = options;
    //响应式
    new Observer(this.$data);
    //编译模板
    new Compiler(this.$el,this);
    //挂在this
    // this.proxyData(this.$data);
  }
  proxyData(data){
    Object.keys(data).forEach(key => {
      Object.defineProperty(this,key ,{
        get(){
          return data[key];
        },
        set(newVal){
          data[key] = newVal;
        }
      })
    })
  }
}
//一个数据一个dep
class Dep{
  constructor(){
    this.collect = [];
  }
  addWatcher(watcher){
    this.collect.push(watcher)
  }
  notify(){
    this.collect.forEach(w=>{
      w.update();
    })
  }
}
//一个dom引用一个watcher
class Watcher{
  constructor(expr,vm,cellBack){
    this.expr = expr;
    this.vm = vm;
    this.cellBack = cellBack;
    this.oldValue = this.getOldValue();
  }
  getOldValue(){
    
    Dep.target = this;
    const oldValue = utils.getValue(this.expr,this.vm);
    Dep.target = null;
    return oldValue;
  }
  update(){
    const newValue = utils.getValue(this.expr,this.vm);
    // console.log(newValue)
    if(newValue !== this.oldValue){
      this.cellBack(newValue);
    }
  }
}
class Observer{
  constructor(data){
    this.observe(data);
  }
  observe(data){
    if(data && typeof data === "object"){
      Object.keys(data).forEach(key =>{
        this.defineReactive(data,key,data[key]);
      })
    }
  }
  defineReactive(obj,key,value){
    this.observe(value);
    const dep = new Dep();
    Object.defineProperty(obj,key,{
      get(){
        const target = Dep.target;
        // console.log(Dep.target)
        Dep.target && dep.addWatcher(target);
        return value;
      },
      set:(newVal)=>{
        if(newVal === value)return;
        //如果value={} 继续递归
        this.observe(value);
        value = newVal;
        dep.notify();
        console.log("set"+newVal)
        console.log(dep)
      }
    })
  }
}
class Compiler{
  constructor(el,vm){
    this.el = this.isElementNode(el) ? el : document.querySelector(el);
    this.vm = vm;
    //创建片段
    const fragment = this.compilerFragment(this.el);
    //渲染
    this.compile(fragment);
    //上树
    this.el.appendChild(fragment)
  }
  compile(fragment){
    const childNodes = Array.from(fragment.childNodes);
    childNodes.forEach(node =>{
      if(this.isElementNode(node)){
        //标签
        this.compileElement(node);
      }else if(this.isTextNode(node)){
        //文本
        // console.log(node)
        this.compileText(node);
      }
      if(node.childNodes && node.childNodes.length){
        this.compile(node);
      }
    })
  }
  compileElement(el){
    const attributes = Array.from(el.attributes);
    attributes.forEach(attr =>{
      const {name,value}=attr;
      if(this.isDirector(name)){
        const[,directive] = name.split('-');
        const [compileKey,eventName] = directive.split(':');
        //compileKey = model,
        utils[compileKey](el,value,this.vm,eventName);
      }else if(this.isEventName(name)){
        //@
        const [,eventName] = name.split('@')
        utils['on'](el,value,this.vm,eventName);
      }
    })
  }
  compileText(el){
    const content = el.textContent;
    if(/\{\{(.+)\}\}/.test(content)){
      utils['text'](el,content,this.vm)
    }
  }
  compilerFragment(el){
    const f = document.createDocumentFragment();
    let firstChild;
    while(firstChild = el.firstChild){
      f.appendChild(firstChild)
    }
    return f;
  }
  isEventName(name){
    return name.startsWith('@')
  }
  isDirector(name){
    return name.startsWith('v-');
  }
  isElementNode(el){
    return el.nodeType === 1;
  }
  isTextNode(el){
    return el.nodeType === 3;
  }
}
const utils = {
  getValue(expr,vm){
    //触发gei
    return vm.$data[expr.trim()];
  },
  setValue(expr,vm,newValue){
    vm.$data[expr] = newValue;
  },
  model(node,value,vm){
    const val = this.getValue(value,vm);
    new Watcher(value,vm,(newValue)=>{
      this.modelUpdater(node,newValue);
    })
    node.addEventListener('input',(e)=>{
      const newValue = e.target.value;
      console.log(newValue)
      this.setValue(value,vm,newValue);
    })
    this.modelUpdater(node,val);
  },
  text(node,value,vm){
    let result;
    if(value.includes('{{')){
      result = value.replace(/\{\{(.+)\}\}/,(...args)=>{
        const expr = args[1]
        new Watcher(expr,vm,(newVal)=>{
          this.textUpdater(node,newVal)
        })
        return this.getValue(args[1],vm);
      });
    }else{
      result = this.getValue(value,vm);
    }
    this.textUpdater(node,result);
  },
  on(node,value,vm,eventName){
    if(!eventName)return;
    const fn = vm.$options.methods[value];
    console.log(fn)
    console.log(eventName)
    node.addEventListener(eventName,fn.bind(vm),false)
  },
  textUpdater(node,value){
    node.textContent = value;
  },
  modelUpdater(node,value){
    node.value = value;
  }
}
window.Vue =Vue;